package io.github.junxworks.qt.modules.mm.service.impl;

import java.util.Date;
import java.util.List;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import org.apache.shiro.SecurityUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.google.common.collect.Lists;
import com.google.common.collect.Sets;

import io.github.junxworks.ep.auth.model.UserModel;
import io.github.junxworks.ep.core.exception.BusinessException;
import io.github.junxworks.ep.qlexp.CheckResult;
import io.github.junxworks.ep.qlexp.RuleEngine;
import io.github.junxworks.ep.sys.constants.RecordStatus;
import io.github.junxworks.ep.sys.constants.YesNo;
import io.github.junxworks.junx.core.exception.BaseRuntimeException;
import io.github.junxworks.junx.core.util.StringUtils;
import io.github.junxworks.qt.constants.QtConstants;
import io.github.junxworks.qt.modules.mm.dto.ExpAnalyzeResult;
import io.github.junxworks.qt.modules.mm.dto.MmVariableCondition;
import io.github.junxworks.qt.modules.mm.dto.MmVariableDto;
import io.github.junxworks.qt.modules.mm.entity.MmVarDsRela;
import io.github.junxworks.qt.modules.mm.entity.MmVarVarRela;
import io.github.junxworks.qt.modules.mm.entity.MmVariable;
import io.github.junxworks.qt.modules.mm.mapper.MmVariableMapper;
import io.github.junxworks.qt.modules.mm.service.MmDatastreamService;
import io.github.junxworks.qt.modules.mm.service.MmVariableService;
import io.github.junxworks.qt.modules.mm.vo.MmDatastreamVo;
import io.github.junxworks.qt.modules.mm.vo.MmVariableVo;

@Service
public class MmVariableServiceImpl implements MmVariableService {

	@Autowired
	private MmVariableMapper mmVariableMapper;

	@Autowired
	private MmDatastreamService datastreamService;

	@Autowired
	private RuleEngine engine;

	@Override
	public List<MmVariableVo> queryList(MmVariableCondition condition) {
		return mmVariableMapper.queryList(condition);
	}

	@Override
	public MmVariableVo queryMmVariableById(Long id) {
		return mmVariableMapper.queryMmVariableById(id);
	}

	@Override
	@Transactional
	public void save(MmVariableDto dto) {
		if (StringUtils.notNull(dto.getName())) {
			MmVariableVo _m = mmVariableMapper.queryMmVariableByName(dto.getName());
			if (_m != null) {
				if (dto.getId() == null || !_m.getId().equals(dto.getId())) {
					throw new BusinessException("已经存在指标" + _m.getId() + "[" + dto.getName() + "]");
				}
			}
		}
		MmVariable entity = new MmVariable();
		BeanUtils.copyProperties(dto, entity);
		UserModel user = (UserModel) SecurityUtils.getSubject().getPrincipal();
		Long userId = user.getId();
		if (entity.getId() == null) {
			entity.setCreateTime(new Date());
			entity.setCreateUser(userId);
			entity.setStatus(RecordStatus.NORMAL.getValue());
			mmVariableMapper.insertWithoutNull(entity);
		} else {
			MmVariable m = mmVariableMapper.selectEntityByID(MmVariable.class, entity.getId());
			if (!userId.equals(m.getCreateUser())) {
				throw new BusinessException("不是创建者不能操作数据");
			}
			entity.setUpdateTime(new Date());
			entity.setUpdateUser(userId);
			mmVariableMapper.updateWithoutNull(entity);
			mmVariableMapper.deleteVarDsRela(entity.getId());
			mmVariableMapper.deleteVarVarRela(entity.getId());
		}
		if (entity.getStatus() == null || RecordStatus.DELETED.getValue() != entity.getStatus()) {
			Long varId = entity.getId();
			if (QtConstants.STAT_DATA_SOURCE_DS.equals(entity.getType())) {
				MmVarDsRela vdr = new MmVarDsRela();
				vdr.setVarId(varId);
				vdr.setDsId(entity.getDsId());
				mmVariableMapper.insertWithoutNull(vdr);
			} else if (QtConstants.STAT_DATA_SOURCE_EXP.equals(entity.getType())) {
				String expression = entity.getExpression();
				if (expression.contains(QtConstants.FUNC_NAME_EXP)) {
					throw new BusinessException("统计表达式不能引用表达式函数");
				}
				if (StringUtils.notNull(expression)) {
					ExpAnalyzeResult ares;
					try {
						ares = analyzeExp(expression);
					} catch (Exception e) {
						throw new BaseRuntimeException(e);
					}
					//数据流解析
					mmVariableMapper.insertBatch(ares.getDs().stream().flatMap(dsName -> {
						MmDatastreamVo ds = datastreamService.queryDataStreamByName(dsName);
						if (ds == null) {
							throw new WrongExpressionException("不是有效的数据流\"" + dsName + "\"");
						}
						if(YesNo.NO.getValue()==ds.getExpable()) {
							throw new WrongExpressionException("数据流\"" + dsName + "\"不能用于表达式");
						}
						MmVarDsRela vdr = new MmVarDsRela();
						vdr.setVarId(varId);
						vdr.setDsId(ds.getId());
						return Stream.of(vdr);
					}).collect(Collectors.toList()));
					//变量解析
					mmVariableMapper.insertBatch(ares.getVariables().stream().flatMap(vName -> {
						MmVariableVo var = queryMmVariableByName(vName);
						if (var == null) {
							throw new WrongExpressionException("不是有效的指标\"" + vName + "\"");
						}
						MmVarVarRela vvr = new MmVarVarRela();
						vvr.setVarIdFrom(varId);
						vvr.setVarIdTo(var.getId());
						return Stream.of(vvr);
					}).collect(Collectors.toList()));
					queryAllRelatedVars(varId);
				}
			}
		}
	}

	public ExpAnalyzeResult analyzeExp(String exp) throws Exception {
		final ExpAnalyzeResult res = new ExpAnalyzeResult();
		if (StringUtils.isNull(exp)) {
			throw new WrongExpressionException("表达式不能为空");
		}
		CheckResult cr = engine.checkExpression(exp);
		if (!cr.isPass()) {
			throw new WrongExpressionException(cr.getMessage());
		}
		QtConstants.INNER_FUNCTIONS.forEach(f -> {
			String pattern = f + "\\s*\\(.+?\\)";
			Pattern r = Pattern.compile(pattern);
			Matcher m = r.matcher(exp);
			while (m.find()) {
				String matched = m.group(0);
				matched = StringUtils.replace(matched, " ", "");
				String sub = matched.substring(matched.indexOf("(") + 2, matched.indexOf(")") - 1);
				res.addRela(f, sub);
			}
		});
		return res;
	}

	public Set<Long> queryAllRelatedVars(Long varId) {
		Set<Long> relaIds = Sets.newHashSet(mmVariableMapper.queryRelaVarIdsByVarId(varId));
		if (relaIds != null && !relaIds.isEmpty()) {
			for (Long id : relaIds) {
				relaIds.addAll(queryRelatedExpression(id, varId));
			}
		}
		return relaIds;
	}

	private Set<Long> queryRelatedExpression(Long varId, Long rootVarId) {
		Set<Long> relaIds = Sets.newHashSet(mmVariableMapper.queryRelaVarIdsByVarId(varId));
		if (relaIds != null && !relaIds.isEmpty()) {
			if (relaIds.contains(rootVarId)) {
				throw new WrongExpressionException("指标循环引用[" + mmVariableMapper.queryMmVariableById(varId).getName() + "]");
			}
			for (Long id : relaIds) {
				relaIds.addAll(queryRelatedExpression(id, rootVarId));
			}
		}
		return relaIds;
	}

	@Override
	public MmVariableVo queryMmVariableByName(String name) {
		return mmVariableMapper.queryMmVariableByName(name);
	}

	@Override
	public String checkVarCanDelete(Long id) {
		String res = "";
		String expRela = mmVariableMapper.queryRelaExpNamesByVarId(id);
		if (StringUtils.notNull(expRela)) {
			res += "关联表达式[" + expRela + "] ";
		}
		String varRela = mmVariableMapper.queryRelaVarNamesByExpId(id);
		if (StringUtils.notNull(varRela)) {
			res += "关联指标[" + varRela + "] ";
		}
		return res;
	}

	@Override
	public List<MmVariableVo> queryAllVariableByVarIds(List<Long> varIds) {
		Set<Long> ids = Sets.newHashSet(varIds);
		for (Long id : varIds) {
			ids.addAll(queryAllRelatedVars(id));
		}
		return mmVariableMapper.queryAllVariablesByVarIds(Lists.newArrayList(ids));
	}

	@Override
	public List<Long> queryAllDatastreamByVarIds(List<Long> varIds) {
		Set<Long> ids = Sets.newHashSet(varIds);
		for (Long id : varIds) {
			ids.addAll(queryAllRelatedVars(id));
		}
		return mmVariableMapper.queryAllDatastreamIdsByVarIds(Lists.newArrayList(ids));
	}
}
